from datetime import datetime
import logging
from core.entities.account_role import AccountWrap
from extensions.ext_database import db

from models.robot.rob_charater import RobCharacter
from models.robot.rob_emoji import RobEmojiPack
from models.robot.rob_timbre import RobTimbre
from services.errors.character import CharacterNotExistError
from services.errors.emoji import EmojiPackageNotExistError
from models.robot.rob_domain import RobFunDomain,RobFunDomainMap,RobFuncs
from flask_babel import _

from services.errors.fun_domain import FunDomainNotExistError, FunDomainParamError, FunNotExistError
from services.errors.timbre import TimbreNotExistError
from services.log_service import LogAction, LogService
class FunDomainService:
    @staticmethod
    def get_domain_list(tenant_id,args:dict)->dict:
        try:
            query = db.session.query(RobFunDomain).filter(RobFunDomain.tenant_id==tenant_id)
            total = 0 
            has_more = False
            if "id" in args and args["id"]:
                query = query.filter(RobFunDomain.id==args["id"])
                total = query.count()
            else:
                if "name" in args and args["name"]:
                    query = query.filter(RobFunDomain.name.ilike(f"%{args['name']}%"))
                if "default_character_id" in args and args["default_character_id"]:
                    query = query.filter(RobFunDomain.default_character_id==args["default_character_id"])
                if "default_timbre_id" in args and args["default_timbre_id"]:
                    query = query.filter(RobFunDomain.default_timbre_id==args["default_timbre_id"])
                if "default_emoji_pack_id" in args and args["default_emoji_pack_id"]:
                    query = query.filter(RobFunDomain.default_emoji_pack_id==args["default_emoji_pack_id"])
                total = query.count()
                if "page" in args and args["page"]>0 and "page_size" in args and args["page_size"]>0:
                    query = query.offset((args["page"]-1)*args["page_size"]).limit(args["page_size"])
                    has_more = total > args["page"]*args["page_size"]
            domains = query.all()
            return {
                "total":total,
                "has_more":has_more,
                "data":domains
            }
        except Exception as e:
            logging.error(e)
            return {
                "total":0,
                "has_more":False,
                "data":[],
                "message":str(e),
            }

                
    @staticmethod
    def create_domain(account:AccountWrap,args:dict,ip_addr:str)->RobFunDomain:
        if not account or not isinstance(account, AccountWrap):
            raise NotLoginError()
        tenant_id = account.current_tenant_id
        name = args.get("name",None)
        descrip = args.get("descrip",None)
        default_character_id = args.get("default_character_id",None)
        default_emoji_pack_id = args.get("default_emoji_pack_id",None)
        default_timbre_id = args.get("default_timbre_id",None)
        price = args.get("price",0.0)
        if price >0:
            price = int(price*1000)
        func_ids = args.get("funcs",[]) if args.get("funcs",None) else []
        character_ids = args.get("characters",[]) if args.get("characters",None) else []
        emoji_pack_ids = args.get("emoji_packs",[]) if args.get("emoji_packs",None) else []
        timbre_ids = args.get("timbres",[]) if args.get("timbres",None) else []
        try:
            same_name_domain = db.session.query(RobFunDomain).filter(RobFunDomain.name==name).one_or_none()
            if same_name_domain:
                raise FunDomainParamError(_("Fun Domain name already exists. {name}").format(name=name))

            for character_id in character_ids:
                character = db.session.get(RobCharacter,character_id)
                if not character:
                    raise CharacterNotExistError(character_id)
                
            if default_character_id:
                default_character = db.session.get(RobCharacter,default_character_id)
                if not default_character:
                    raise CharacterNotExistError(default_character_id)
                if default_character_id not in character_ids:
                    character_ids.append(default_character_id)
                if not default_timbre_id:
                    default_timbre_id = default_character.default_timbre_id
                if not default_emoji_pack_id:
                    default_emoji_pack_id = default_character.default_emoji_pack_id
            else:
                raise FunDomainParamError(_("Character ID cannot be empty"))

            for timbre_id in timbre_ids:
                timbre = db.session.get(RobTimbre,timbre_id)
                if not timbre:
                    raise TimbreNotExistError(timbre_id)
            if default_timbre_id:
                default_timbre = db.session.get(RobTimbre,default_timbre_id)
                if not default_timbre:
                    raise TimbreNotExistError(default_timbre_id)
                if default_timbre_id not in timbre_ids:
                    timbre_ids.append(default_timbre_id)
            else:
                raise FunDomainParamError(_("Timbre ID cannot be empty"))
            
            

            for emoji_pack_id in emoji_pack_ids:
                emoji_pack = db.session.get(RobEmojiPack,emoji_pack_id)
                if not emoji_pack:
                    raise EmojiPackageNotExistError(emoji_pack_id)
            if default_emoji_pack_id:
                default_emoji_pack = db.session.get(RobEmojiPack,default_emoji_pack_id)
                if not default_emoji_pack:
                    raise EmojiPackageNotExistError(default_emoji_pack_id)
                if default_emoji_pack_id not in emoji_pack_ids:
                    emoji_pack_ids.append(default_emoji_pack_id)
            else:
                raise FunDomainParamError(_("Emoji package ID cannot be empty"))
            
            for func_id in func_ids:
                if not db.session.get(RobFuncs,func_id):
                    raise FunNotExistError(func_id)
            
            fun_domain = RobFunDomain(
                tenant_id = tenant_id, # type: ignore
                name = name,# type: ignore
                descrip = descrip,# type: ignore
                default_character_id = default_character_id,# type: ignore
                default_timbre_id = default_timbre_id,# type: ignore
                default_emoji_pack_id = default_emoji_pack_id,# type: ignore
                price = price,# type: ignore
                created_by = account.id,# type: ignore
            )
            db.session.add(fun_domain)
            db.session.flush()
            fun_domain.timbres= timbre_ids
            fun_domain.emoji_packs = emoji_pack_ids
            characters = db.session.query(RobCharacter).filter(RobCharacter.id.in_(character_ids)).all()
            fun_domain.characters = characters

            for func_id in func_ids:
                db.session.add(RobFunDomainMap(
                    domain_id = fun_domain.id,# type: ignore
                    func_id = func_id # type: ignore
                ))
            LogService.log(
                tenant_id = tenant_id,
                account_id= account.id,
                action = LogAction.CREATE_FUN_DOMAIN,
                content = f"{account.name} create fun domain {fun_domain.name}",
                created_ip = ip_addr,
                submit = False
            )
            db.session.commit()
            return fun_domain
        except Exception as e:
            db.session.rollback()
            raise e


    @staticmethod
    def update_domain(account:AccountWrap,args:dict,ip_addr:str)->RobFunDomain:
        if not account or not isinstance(account, AccountWrap):
            raise NotLoginError()
        tenant_id = account.current_tenant_id
        domain_id = args.get("id",None)
        name = args.get("name",None)
        descrip = args.get("descrip",None)
        default_character_id = args.get("default_character_id",None)
        default_emoji_pack_id = args.get("default_emoji_pack_id",None)
        default_timbre_id = args.get("default_timbre_id",None)
        price = args.get("price",0.0)
        if price >0:
            price = int(price*1000)
        func_ids = args.get("funcs",[]) if args.get("funcs",None) else []
        character_ids = args.get("characters",[]) if args.get("characters",None) else []
        emoji_pack_ids = args.get("emoji_packs",[]) if args.get("emoji_packs",None) else []
        timbre_ids = args.get("timbres",[]) if args.get("timbres",None) else []
        if not domain_id:
            raise FunDomainParamError(_("Domain ID cannot be empty"))
        try:
            domain = db.session.get(RobFunDomain,domain_id)
            if not domain:
                raise FunDomainNotExistError(domain_id)
            
            for timbre_id in timbre_ids:
                timbre = db.session.get(RobTimbre,timbre_id)
                if not timbre:
                    raise TimbreNotExistError(timbre_id)
            if default_timbre_id:
                default_timbre = db.session.get(RobTimbre,default_timbre_id)
                if not default_timbre:
                    raise TimbreNotExistError(default_timbre_id)
                if default_timbre_id not in timbre_ids:
                    timbre_ids.append(default_timbre_id)
            else:
                raise FunDomainParamError(_("Timbre ID cannot be empty"))
            
            for character_id in character_ids:
                character = db.session.get(RobCharacter,character_id)
                if not character:
                    raise CharacterNotExistError(character_id)
            if default_character_id:
                default_character = db.session.get(RobCharacter,default_character_id)
                if not default_character:
                    raise CharacterNotExistError(default_character_id)
                if default_character_id not in character_ids:
                    character_ids.append(default_character_id)
            else:
                raise FunDomainParamError(_("Character ID cannot be empty"))
            
            for emoji_pack_id in emoji_pack_ids:
                emoji_pack = db.session.get(RobEmojiPack,emoji_pack_id)
                if not emoji_pack:
                    raise EmojiPackageNotExistError(emoji_pack_id)
            if default_emoji_pack_id:
                default_emoji_pack = db.session.get(RobEmojiPack,default_emoji_pack_id)
                if not default_emoji_pack:
                    raise EmojiPackageNotExistError(default_emoji_pack_id)
                if default_emoji_pack_id not in emoji_pack_ids:
                    emoji_pack_ids.append(default_emoji_pack_id)
            else:
                raise FunDomainParamError(_("Emoji package ID cannot be empty"))
            
            for func_id in func_ids:
                if not db.session.get(RobFuncs,func_id):
                    raise FunNotExistError(func_id)
            
            domain.name = name
            domain.descrip = descrip
            domain.price = price
            domain.default_character_id = default_character_id
            domain.default_timbre_id = default_timbre_id
            domain.default_emoji_pack_id = default_emoji_pack_id
            domain.updated_at = datetime.now()
            domain.updated_by = account.id
            domain.timbres= timbre_ids
            domain.emoji_packs = emoji_pack_ids
            characters = db.session.query(RobCharacter).filter(RobCharacter.id.in_(character_ids)).all()
            domain.characters = characters

            funcs = db.session.query(RobFunDomainMap).filter(RobFunDomainMap.domain_id==domain.id).all()
            exist_func_ids = [str(func.func_id) for func in funcs]
            for func in funcs:#[ABCD] =>[ABE]=>DEL[CD]
                if str(func.func_id) not in func_ids:
                    db.session.delete(func)
            new_func_ids = [func_id for func_id in func_ids if func_id not in exist_func_ids]
                    
            for func_id in new_func_ids:
                db.session.add(RobFunDomainMap(
                    domain_id = fun_domain.id,# type: ignore
                    func_id = func_id # type: ignore
                ))

            # flag_modified(domain, "config")
            LogService.log(
                tenant_id = tenant_id,
                account_id= account.id,
                action = LogAction.UPDATE_FUN_DOMAIN,
                content = f"{account.name} update fun domain {domain.name}",
                created_ip = ip_addr,
                submit = False
            )
            db.session.commit()
            return domain
        except Exception as e:
            db.session.rollback()
            raise e


    @staticmethod
    def del_domain(account:AccountWrap,fun_domain_id:str|None,ip_addr:str):
        if not fun_domain_id:
            raise FunDomainParamError(_("Fun domain ID cannot be empty"))
        fun_domain = db.session.get(RobFunDomain,fun_domain_id)
        if not fun_domain:
            raise FunDomainNotExistError(fun_domain_id)
        try:
            log_msg = f"{account.name} delete fun domain:{fun_domain.id}:{fun_domain.name}"
            db.session.delete(fun_domain)
            db.session.query(RobFunDomainMap).filter(RobFunDomainMap.domain_id==fun_domain.id).delete()
            LogService.log(
                account.current_tenant_id,
                account.id,
                LogAction.DEL_FUN_DOMAIN,
                log_msg,
                ip_addr,
                submit=False
            )
            db.session.commit()
        except Exception as e:
            db.session.rollback()
            raise e